import sys

import numpy as np

import okex.v5.account_api as account
import okex.v5.market_api as market
import okex.v5.public_api as public
import okex.v5.trade_api as trade
import json
import datetime
import time
import pandas as pd
from okex.client_old import *
import matplotlib.pyplot as plt
import warnings
import requests, json
from okex.v5.ccytype import CcyType
from okex.v5.trade_api import TdMode, OrderType

# 钉钉机器人设置
headers = {'Content-Type': 'application/json'}  # 定义数据类型
webhook = 'https://oapi.dingtalk.com/robot/send?access_token=c352654244a20c1b96c026cf045d21164f56f517c047c1a9c8ca1421ac13af42'


# 获取币种余额信息
def accquire_exact_coin_balance(api_key, secret_key, passphrase, coin_name, test=True):
    # 资金账户API
    accountAPI = account.AccountAPI(api_key, secret_key, passphrase, False, test=test)
    account_left = []
    ## 查看账户余额
    while account_left == None:
        try:
            account_left = accountAPI.balance(ccyType=CcyType.USDT)
        except:
            time.sleep(5)
    account_left_keys = [coin_dict['ccy'] for coin_dict in account_left[0]['details']]
    account_left_values = account_left[0]['details']
    accout_left_dict = dict(zip(account_left_keys, account_left_values))

    return float(accout_left_dict[coin_name]['availBal'])


def call_grid_strategy(api_key, secret_key, passphrase, current_price_grid, test=True):
    # 资金账户API
    accountAPI = account.AccountAPI(api_key, secret_key, passphrase, False, test=test)
    ## 查看账户余额
    result_account = accountAPI.balance(ccyType=CcyType.USDT)

    # 市场行情API
    marketAPI = market.MarketAPI(api_key, secret_key, passphrase, False, test=test)
    marketAPI.set_api_url("https://www.okx.com")


    # trade
    tradeAPI = trade.TradeAPI(api_key, secret_key, passphrase, False, test=test)

    account_keys = [coin_dict['ccy'] for coin_dict in result_account[0]['details']]
    account_values = result_account[0]['details']
    accout_dict = dict(zip(account_keys, account_values))

    count = 0

    # 策略开始时间
    start_time = time.time()

    # 网格选择多少天更新一次
    updating_days = 10

    # 钉钉test
    start_grid_price = str(current_price_grid)
    data = {
        "msgtype": "text",
        "text": {
            "content": "张灿好帅,网格起点为:" + start_grid_price
        },
        "at": {
            "atMobiles": [
                "16620498667",
            ],
            "isAtAll": False
        }
    }

    requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求

    # 网格设定
    ##上,下网格数量
    half_grid_number = 5

    ##资金涨跌幅,决定网格上下限
    high_low_range = 0.03

    ##步长
    grid_step = high_low_range / half_grid_number

    top_grid_list = current_price_grid * np.arange(1, 1 + high_low_range, grid_step)
    down_grid_list = current_price_grid * np.arange(1, 1 - high_low_range, -grid_step)[1:]
    total_grid_list = sorted(list(top_grid_list) + list(down_grid_list))
    print(total_grid_list)

    # 决定策略投入比例
    using_money_share = 1
    using_money = float(accout_dict['USDT']['availBal']) * using_money_share
    print("期初策略投入现金额为{} 美元".format(using_money))

    # 钉钉推送
    initial_invested_money = str(using_money)
    data = {
        "msgtype": "text",
        "text": {
            "content": "张灿好帅,期初策略投入现金额为:" + initial_invested_money
        },
        "at": {
            "atMobiles": [
                "16620498667",
            ],
            "isAtAll": False
        }
    }

    requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求

    if float(accout_dict['USDT']['availBal']) > 0:
        # 可买入总币数
        can_holding_bitcoin = using_money / current_price_grid
        print("可买入比特币总数:{}".format(can_holding_bitcoin))
        # 买入1份对应的真实买入币数
        buy_one_part = can_holding_bitcoin / (2 * half_grid_number)

        btc_start_before = accquire_exact_coin_balance(api_key, secret_key, passphrase, 'BTC', test=test)
        # 网格起点买入给定网格数量的btc
        rt = tradeAPI.order('BTC-USDT', TdMode.CASH, OrderType.FOK, px=current_price_grid,
                            sz=+half_grid_number * buy_one_part)
        print(rt)
        btc_start_after = accquire_exact_coin_balance(api_key, secret_key, passphrase, 'BTC', test=test)
        # 买入失败持续挂单,直到成交
        while (int(rt[0]['sCode']) != 0) or (btc_start_after == btc_start_before):
            # 网格起点买入一半总份额
            rt = tradeAPI.order('BTC-USDT', TdMode.CASH, OrderType.FOK, px=current_price_grid,
                                sz=+half_grid_number * buy_one_part)
            print(rt)
            btc_start_after = accquire_exact_coin_balance(api_key, secret_key, passphrase, 'BTC', test=test)
            time.sleep(1)
        print('期初买入成功,总花费{}'.format(half_grid_number * buy_one_part * current_price_grid))
        print('买入的一份比特币额为{}'.format(buy_one_part))
        print('买入比特币总额为{}'.format(half_grid_number * buy_one_part))
    else:
        print("当前余额不足")
        exit(-1)

    # 钉钉推送
    total_init_buy_money = str(half_grid_number * buy_one_part * current_price_grid)
    buy_one_part_bitcoin = str(buy_one_part)
    buy_total_bitcoin = str(half_grid_number * buy_one_part)
    data = {
        "msgtype": "text",
        "text": {
            "content": "张灿好帅,期初买入总花费为:" + total_init_buy_money + "\n" + "买入的一份比特币额为:" + buy_one_part_bitcoin + "\n" + "买入比特币总额为:" + buy_total_bitcoin
        },
        "at": {
            "atMobiles": [
                "16620498667",
            ],
            "isAtAll": False
        }
    }

    requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求

    # 是否初始化
    initiate = True

    # taker单
    buy_count = 0
    buy_fee = 0

    # post_only可能导致交易经常失败,实盘时建议采用

    sell_count = 0
    sell_fee = 0

    # 总网格利润
    net_profit = 0

    # 单网格收益
    single_grid_profit = current_price_grid * grid_step * buy_one_part

    # first price
    first_price_list = []
    while True:
        # USDT, BTC 余额
        usdt_left_before = accquire_exact_coin_balance(api_key, secret_key, passphrase, 'USDT', test=test)
        btc_left_before = accquire_exact_coin_balance(api_key, secret_key, passphrase, 'BTC', test=test)

        # 是否有交易
        traded = False
        count += 1
        try:
            current_price = marketAPI.ticker("BTC-USDT").loc[0, 'last']
        except:
            time.sleep(10)

        if initiate:
            first_price_list.append(current_price)
            first_price = current_price
            second_price = current_price
            initiate = False
            # 上一次成交线
            last_deal_line = current_price_grid
        else:
            ## 只有成交了才会变first price
            first_price = first_price_list[0]
            second_price = current_price
        current_total_ustd = usdt_left_before + btc_left_before * current_price

        # 判断上穿下穿,穿了几根线,穿了几根就买入或卖出几份
        go_through_direction = None
        go_through_number = 0
        possible_deal_line = None
        for grid_line in total_grid_list:
            ## 上穿的 deal line 为最后一个grid line,下穿的deal line为第一个进入判断的deal line
            if (first_price < grid_line <= second_price):
                go_through_direction = "up"
                go_through_number += 1
                possible_deal_line = grid_line
            elif (first_price > grid_line >= second_price):
                go_through_direction = "down"
                if go_through_number == 0:
                    possible_deal_line = grid_line
                go_through_number += 1

        # 若上穿某根线,则卖穿过份额数量的BTC
        if (go_through_direction == "up") & (possible_deal_line != last_deal_line):
            try:
                rt = tradeAPI.order('BTC-USDT', TdMode.CASH, OrderType.FOK, px=second_price,
                                    sz=-buy_one_part * go_through_number)
                print("卖出:", rt)
                print("\n")

                # 钉钉推送
                data = {
                    "msgtype": "text",
                    "text": {
                        "content": "张灿好帅 " + "卖出结果为: " + str(rt) + "\n" + "卖出份额为: " + str(go_through_number)
                    },
                    "at": {
                        "atMobiles": [
                            "16620498667",
                        ],
                        "isAtAll": False
                    }
                }
                requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求

                # USDT 余额是否增加来判断是否买入成功,不成功,则保留first price 不更新
                usdt_left_sold = accquire_exact_coin_balance(api_key, secret_key, passphrase, 'USDT', test=test)
                if (int(rt[0]['sCode']) == 0) & (usdt_left_sold > usdt_left_before):
                    traded = True
                    sell_count += 1
                    last_deal_line = possible_deal_line
                    net_profit += single_grid_profit

                    # USDT, BTC 余额
                    ## 查看账户余额

                    usdt_left_after = accquire_exact_coin_balance(api_key, secret_key, passphrase, 'USDT', test=test)
                    btc_left_after = accquire_exact_coin_balance(api_key, secret_key, passphrase, 'BTC', test=test)

                    current_total_ustd = usdt_left_after + btc_left_after * second_price
                    # sell fee
                    sell_fee += buy_one_part * second_price * go_through_number - (usdt_left_after - usdt_left_before)
                    actual_sell_fee_rate = float(
                        (buy_one_part * second_price * go_through_number - (usdt_left_after - usdt_left_before)) / (
                                buy_one_part * second_price * go_through_number)) * 100
                    # 钉钉推送
                    data = {
                        "msgtype": "text",
                        "text": {
                            "content": "张灿好帅 " + "实际卖出费率为: " + str(actual_sell_fee_rate) + "%"
                        },
                        "at": {
                            "atMobiles": [
                                "16620498667",
                            ],
                            "isAtAll": False
                        }
                    }
                    requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求

                    first_price_list[0] = second_price

                    # 上穿网格,全部卖出并等待再次调用
                elif int(rt[0]['sCode']) == 51008:
                    rt = tradeAPI.order('BTC-USDT', TdMode.CASH, OrderType.MARKET, sz=-btc_left_before)
                    print("上穿网格卖出结果:", rt)
                    print("\n")

                    # 钉钉推送
                    data = {
                        "msgtype": "text",
                        "text": {
                            "content": "张灿好帅 " + "上穿网格卖出结果为" + str(rt)
                        },
                        "at": {
                            "atMobiles": [
                                "16620498667",
                            ],
                            "isAtAll": False
                        }
                    }
                    requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求
                    return 1
            except:
                time.sleep(5)


        # 若下穿某根线,则买入一份额数量的BTC
        elif (go_through_direction == "down") & (possible_deal_line != last_deal_line):
            try:
                if second_price <= total_grid_list[0]:
                    rt = tradeAPI.order('BTC-USDT', TdMode.CASH, OrderType.MARKET, sz=+usdt_left_before)
                else:
                    rt = tradeAPI.order('BTC-USDT', TdMode.CASH, OrderType.FOK, px=second_price,
                                        sz=+buy_one_part * go_through_number)
                print("买入:", rt)
                print("\n")
                # 钉钉推送
                data = {
                    "msgtype": "text",
                    "text": {
                        "content": "张灿好帅 " + "买入结果为" + str(rt) + "\n" + "买入份额为: " + str(go_through_number)
                    },
                    "at": {
                        "atMobiles": [
                            "16620498667",
                        ],
                        "isAtAll": False
                    }
                }
                requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求
                # BTC 余额是否增加来判断是否买入成功
                btc_left_buy = accquire_exact_coin_balance(api_key, secret_key, passphrase, 'BTC', test=test)

                if (int(rt[0]['sCode']) == 0) & (btc_left_buy > btc_left_before):
                    traded = True
                    buy_count += 1
                    last_deal_line = possible_deal_line

                    # USDT, BTC 余额
                    ## 查看账户余额
                    try:
                        account_left = accountAPI.balance(ccyType=CcyType.USDT)
                    except:
                        time.sleep(10)

                    usdt_left_after = accquire_exact_coin_balance(api_key, secret_key, passphrase, 'USDT', test=test)
                    btc_left_after = accquire_exact_coin_balance(api_key, secret_key, passphrase, 'BTC', test=test)

                    current_total_ustd = usdt_left_after + btc_left_after * second_price
                    # buy fee
                    buy_fee += buy_one_part * go_through_number - (btc_left_after - btc_left_before)
                    actual_buy_fee_rate = float(
                        (buy_one_part * go_through_number - (btc_left_after - btc_left_before)) / (
                                buy_one_part * go_through_number)) * 100
                    # 钉钉推送
                    data = {
                        "msgtype": "text",
                        "text": {
                            "content": "张灿好帅 " + "买入结果为: " + str(rt) + "\n" + "实际买入费率为: " + str(
                                actual_buy_fee_rate) + "%"
                        },
                        "at": {
                            "atMobiles": [
                                "16620498667",
                            ],
                            "isAtAll": False
                        }
                    }
                    requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求
                    first_price_list[0] = second_price

            except:
                time.sleep(5)

        win_or_loss = current_total_ustd - using_money
        print("\r",
              "初始投资价格为{},当前总价值为{},前一价格为{},当前价格为{},请求次数为{},买入总次数为{},卖出套利总次数为{},当前成交线为{},浮动盈亏为{},套利利润为{},买入费用为{},卖出费用为{}".format(
                  using_money,
                  current_total_ustd,
                  first_price,
                  second_price,
                  count,
                  buy_count,
                  sell_count,
                  last_deal_line,
                  win_or_loss,
                  net_profit,
                  buy_fee,
                  sell_fee),
              end='')

        if traded:
            # 钉钉推送
            total_buy_count = str(buy_count)
            total_sell_count = str(sell_count)
            strategy_profit = str(net_profit)
            current_value = str(current_total_ustd)
            win_or_loss_now = str(win_or_loss)
            current_price_str = str(second_price)

            data = {
                "msgtype": "text",
                "text": {
                    "content": "张灿好帅 " + "买入次数:" + total_buy_count + "\n" + "套利卖出次数:" + total_sell_count + "\n" + "策略收益为:" + strategy_profit + "\n" + "当前总价值为:" + current_value + "\n" + "浮动盈亏为:" + win_or_loss_now + "\n" + "当前价格为:" + current_price_str
                },
                "at": {
                    "atMobiles": [
                        "16620498667",
                    ],
                    "isAtAll": False
                }
            }
            requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求

        sys.stdout.flush()
        time.sleep(0.4)
